package main.java.org.jmtp.sync;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;

import main.java.org.jmtp.sync.model.Track;
import main.java.org.jmtp.sync.model.TrackCallBack;
import main.java.org.jmtp.sync.model.TrackImpl;
import main.java.org.jmtp.sync.model.TrackLocation;
import main.java.org.jmtp.sync.model.TrackManager;

import jmtp.DeviceAlreadyOpenedException;
import jmtp.PortableDevice;
import jmtp.PortableDeviceAudioObject;
import jmtp.PortableDeviceFolderObject;
import jmtp.PortableDeviceManager;
import jmtp.PortableDeviceObject;
import jmtp.PortableDeviceStorageObject;

public class JmtpSync implements TrackCallBack {
	private PortableDevice device;
	private PortableDeviceObject[] rootObjects;
	private TrackManager tm;

	private boolean connected = false;

	private Logger logger = Logger.getLogger("jmtp-sync");
	/**
	 * Constructor with no argument : list all connected device if 
	 * many devices, only one device connected, just connect
	 */
	public JmtpSync (PortableDevice device) throws DeviceAlreadyOpenedException {
		this.device = device;
		tm = new TrackManager();
		/* Open device */
		this.device.open();
		connected = true;
		logger.log(Level.INFO,"Connected to device");
		/* Get root objects (usually folders : Music | Photo |...) */
		this.rootObjects = this.device.getRootObjects();
	}

	/**
	 * This method scan the device for new tracks
	 * @param tcb trackback called on every track
	 */
	public void scanDevice(TrackCallBack tcb) {
		logger.log(Level.INFO,"# root objects: " + this.rootObjects.length);
		if (this.rootObjects.length > 0) {
			for (PortableDeviceObject o : this.rootObjects) {
				logger.log(Level.INFO,"Oops");
				if (getType(o) == ObjectType.Storage) {
					/* In root dir, there could be things that
					 * are not storage, just ignore them
					 */
					recursiveCheck(o, tcb);
				}
				else {
					logger.log(Level.INFO,"Non storage type detected [name:" + o.getName() + ";type:" + getType(o) +"]");
				}
			}
		}
		else {
			logger.log(Level.WARNING, "scanDevice tried with no roots object");
		}
	}
	/**
	 * This method get the type of a PortableDeviceObject
	 * @param pdo : the object
	 * @return ObjectType o : the type of pdo.
	 */
	public ObjectType getType(PortableDeviceObject pdo) {
		if (pdo instanceof PortableDeviceFolderObject) {
			/* Object is a folder */
			return ObjectType.Folder;
		}
		else if (pdo instanceof PortableDeviceAudioObject) {
			/* Object is a mp3 */
			return ObjectType.AudioFile;
		}
		else if (pdo instanceof PortableDeviceStorageObject) {
			/* Object is a storage */
			return ObjectType.Storage;
		}
		else {
			/* File not known */
			return ObjectType.Unknown;
		}
	}

	/**
	 * Scan all childs of a PortableDevieObject with a callback applied 
	 * on each track
	 * @param pdo
	 * @param callBack
	 */
	public void recursiveCheck(PortableDeviceObject pdo, TrackCallBack callBack) {
		PortableDeviceObject[] childs;
		
		switch (getType(pdo)) {
		case Storage:
			childs = ((PortableDeviceStorageObject)pdo).getChildObjects();
			logger.log(Level.INFO, "Folder:" + pdo.getName() + "(" + childs.length + "childs)");
			for(PortableDeviceObject o : childs) {
				/* Recursion */
				recursiveCheck(o,callBack);
			}
			break;
		case Folder:
			childs = ((PortableDeviceFolderObject)pdo).getChildObjects();
			logger.log(Level.INFO, "Folder:" + pdo.getName() + "(" + childs.length + "childs)");
			for(PortableDeviceObject o : childs) {
				/* Recursion */
				recursiveCheck(o,callBack);
			}
			break;
		case AudioFile:
			String name = pdo.getName();
			callBack.cb(pdo);
			logger.log(Level.INFO, "AudioFile:" + name);
			break;
		case Unknown:
			logger.log(Level.WARNING,"recursiveCheck tried to check an unknown type");
			break;
		}

	}

	public void cb(PortableDeviceObject pdo) {
		if (getType(pdo) == ObjectType.AudioFile) {
			tm.saveInDb(new TrackImpl((PortableDeviceAudioObject) pdo,TrackLocation.Device,this.device.getSerialNumber()));
		}
		else {
			logger.log(Level.WARNING,"Callback called on non audio file");
		}
	}

}
